/**
 * @file irq.h
 * @author lcz 3094655619@qq.com
 * @brief about init interupt gate
 * @version 0.1
 * @date 2022-10-16
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "cpu/irq.h"
#include "comm/cpu_instr.h"
#include "os.cfg.h"
#include "tools/log.h"
static gate_desc_t idt_table[GATE_DESC_SIZE];

/**
 * @brief print register frame when happend execption
 *
 * @param frame register frame
 */
static void dump_core_regs(exception_frame_t *frame){

    uint32_t ss, esp;
    if (frame->cs & 0x3)
    {
        // 特权级三，发生了特权级变换,多压入了两个及ss3与esp3
        ss = frame->ss3;
        esp = frame->esp3;
    }
    else
    {
        // 无特权级变化
        ss = frame->ds;
        esp = frame->esp;
    }

    log_printf("IRQ: %d , error_code: %d ",frame->num,frame->error_code);
    log_printf("CS: %d\nDS: %d\nES: %d\nSS: %d\nFS: %d\nGS: %d",
    frame->cs,frame->ds,frame->es,ss,frame->fs,frame->gs);
    log_printf("EAX: 0x%x\n"
               "EBX: 0x%x\n"
               "ECX: 0x%x\n"
               "EDX: 0x%x\n"
               "EDI: 0x%x\n"
               "ESI: 0x%x\n"
               "EBP: 0x%x\n"
               "ESP: 0x%x\n",
               frame->eax, frame->ebx, frame->ecx, frame->edx, frame->edi, frame->esi, frame->ebp,esp);
    log_printf("EIP:0x%x\nEFLAGS:0x%x\n",frame->eip,frame->eflags);
}
/**
 * @brief 异常默认处理韩素华
 */
void do_default_handle(exception_frame_t *frame, const char *message)
{
    log_printf("--------------------");
    log_printf("IRQ/Exception happend %s", message);
    dump_core_regs(frame);
    if (frame->cs & 0x3)
    {
        sys_exit(frame->error_code);
    }
    else
    {
        while (1)
        {
            hlt();
        }
    }
}

// 实现具体的异常处理函数，调用默认异常处理方法
void do_handler_unknown(exception_frame_t *frame)
{
    do_default_handle(frame, "String, but not use now!!!");
}

void do_handler_divider(exception_frame_t *frame)
{
    do_default_handle(frame, "divier exception!!!");
}
void do_handler_Debug(exception_frame_t *frame)
{
    do_default_handle(frame, "Debug Exception");
}

void do_handler_NMI(exception_frame_t *frame)
{
    do_default_handle(frame, "NMI Interrupt.");
}

void do_handler_breakpoint(exception_frame_t *frame)
{
    do_default_handle(frame, "Breakpoint.");
}

void do_handler_overflow(exception_frame_t *frame)
{
    do_default_handle(frame, "Overflow.");
}

void do_handler_bound_range(exception_frame_t *frame)
{
    do_default_handle(frame, "BOUND Range Exceeded.");
}

void do_handler_invalid_opcode(exception_frame_t *frame)
{
    do_default_handle(frame, "Invalid Opcode.");
}

void do_handler_device_unavailable(exception_frame_t *frame)
{
    do_default_handle(frame, "Device Not Available.");
}

void do_handler_double_fault(exception_frame_t *frame)
{
    do_default_handle(frame, "Double Fault.");
}

void do_handler_invalid_tss(exception_frame_t *frame)
{
    do_default_handle(frame, "Invalid TSS");
}

void do_handler_segment_not_present(exception_frame_t *frame)
{
    do_default_handle(frame, "Segment Not Present.");
}

void do_handler_stack_segment_fault(exception_frame_t *frame)
{
    do_default_handle(frame, "Stack-Segment Fault.");
}
/**
 * @brief 通用保护异常
 */
void do_handler_general_protection(exception_frame_t *frame)
{
    log_printf("--------------------------------");
    log_printf("IRQ/Exception happend: General Protection.");
    if (frame->error_code & ERR_EXT)
    {
        log_printf("the exception occurred during delivery of an "
                   "event external to the program, such as an interrupt"
                   "or an earlier exception.");
    }
    else
    {
        log_printf("the exception occurred during delivery of a"
                   "software interrupt (INT n, INT3, or INTO).");
    }

    if (frame->error_code & ERR_IDT)
    {
        log_printf("the index portion of the error code refers "
                   "to a gate descriptor in the IDT");
    }
    else
    {
        log_printf("the index refers to a descriptor in the GDT");
    }

    log_printf("segment index: %d", frame->error_code & 0xFFF8);

    dump_core_regs(frame);
    if (frame->cs & 0x3)
    {
        sys_exit(frame->error_code);
    }
    else
    {
        while (1)
        {
            hlt();
        }
    }
}
/**
 * @brief 页保护异常
 */
void do_handler_page_fault(exception_frame_t *frame)
{
    log_printf("--------------------------------");
    log_printf("IRQ/Exception happend: Page fault.");
    if (frame->error_code & ERR_PAGE_P)
    {
        log_printf("\tpage-level protection violation: 0x%x.", read_cr2());
    }
    else
    {
        log_printf("\tPage doesn't present 0x%x", read_cr2());
    }

    if (frame->error_code & ERR_PAGE_WR)
    {
        log_printf("\tThe access causing the fault was a read.");
    }
    else
    {
        log_printf("\tThe access causing the fault was a write.");
    }

    if (frame->error_code & ERR_PAGE_US)
    {
        log_printf("\tA supervisor-mode access caused the fault.");
    }
    else
    {
        log_printf("\tA user-mode access caused the fault.");
    }

    dump_core_regs(frame);
    // 判断最低两位是否为0，不为0便是应用进程触发的异常，我们kill process
    if(frame->cs&0x3){
        sys_exit(frame->error_code);
    }else {
        while (1)
        {
            hlt();
        }
    }
}

void do_handler_fpu_error(exception_frame_t *frame)
{
    do_default_handle(frame, "X87 FPU Floating Point Error.");
}

void do_handler_alignment_check(exception_frame_t *frame)
{
    do_default_handle(frame, "Alignment Check.");
}

void do_handler_machine_check(exception_frame_t *frame)
{
    do_default_handle(frame, "Machine Check.");
}

void do_handler_smd_exception(exception_frame_t *frame)
{
    do_default_handle(frame, "SIMD Floating Point Exception.");
}

void do_handler_virtual_exception(exception_frame_t *frame)
{
    do_default_handle(frame, "Virtualization Exception.");
}
/**
 * @brief 异常初始化
 */
static void init_pic(void)
{
    // ICW2 开始状态
    // ICW3 芯片级联对应位设置为1
    //  ICW4 忽略嵌套的优先级，控制8259的工作模式
    // 第一块芯片
    outb(PIC0_ICW1, PIC_ICW1_ALWAYS_1 | PIC_ICW1_ICW4);
    outb(PIC0_ICW2, IRQ_PIC_START); //中断其实序号0-7
    outb(PIC0_ICW3, 1 << 2);
    outb(PIC0_ICW4, PIC_ICW4_8086);
    // 第二块芯片
    outb(PIC1_ICW1, PIC_ICW1_ALWAYS_1 | PIC_ICW1_ICW4);
    outb(PIC1_ICW2, IRQ_PIC_START + 8);
    outb(PIC1_ICW3, 2);
    outb(PIC1_ICW4, PIC_ICW4_8086);
    // 中断屏蔽处理器 全部设置为1，全部屏蔽，因为处理程序未实现
    // 从片来的irq2，其他中断屏蔽
    outb(PIC0_IMR, 0xff & ~(1 << 2));
    outb(PIC1_IMR, 0xff);
}

// open and close the interrupt and setting PIC1_IMR register
/**
 * @brief 全局关中断
 */
void irq_disable_global(void)
{
    cli();
}
/**
 * @brief 全局开中断
 */
void irq_enable_gloabl(void)
{
    sti();
}

void irq_enable(int irq_num)
{
    if (irq_num < IRQ_PIC_START)
    {
        return;
    }
    irq_num -= IRQ_PIC_START;
    // 第一块芯片
    if (irq_num < 8)
    {
        uint8_t mask = inb(PIC0_IMR) & ~(1 << irq_num);
        outb(PIC0_IMR, mask);
    }
    else
    {
        // 第二块芯片
        irq_num -= 8;
        uint8_t mask = inb(PIC0_IMR) & ~(1 << irq_num);
        outb(PIC1_IMR, mask);
    }
}

void irq_disable(int irq_num)
{
    if (irq_num < IRQ_PIC_START)
    {
        return;
    }
    irq_num -= IRQ_PIC_START;
    // 第一块芯片
    if (irq_num < 8)
    {
        uint8_t mask = inb(PIC0_IMR) | (1 << irq_num);
        outb(PIC0_IMR, mask);
    }
    else
    {
        // 第二块芯片
        irq_num -= 8;
        uint8_t mask = inb(PIC0_IMR) | (1 << irq_num);
        outb(PIC1_IMR, mask);
    }
}
/**
 * @brief 异常初始化
 */
void irq_init(void)
{
    // clear the interupt first
    for (int i = 0; i < GATE_DESC_SIZE; i++)
    {
        gate_desc_set((gate_desc_t *)&idt_table[i], KERNEL_SELECTOR_CS, (uint32_t)exception_handler_unknown,
                      GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_INT);
    }
    // init divider exception handle func
    irq_install(IRQ0_DE, (irq_handler_t)exception_handler_divider);
    // install other exception handler function
    // 设置异常处理接口
    irq_install(IRQ1_DB, (irq_handler_t)exception_handler_Debug);
    irq_install(IRQ2_NMI, (irq_handler_t)exception_handler_NMI);
    irq_install(IRQ3_BP, (irq_handler_t)exception_handler_breakpoint);
    irq_install(IRQ4_OF, (irq_handler_t)exception_handler_overflow);
    irq_install(IRQ5_BR, (irq_handler_t)exception_handler_bound_range);
    irq_install(IRQ6_UD, (irq_handler_t)exception_handler_invalid_opcode);
    irq_install(IRQ7_NM, (irq_handler_t)exception_handler_device_unavailable);
    irq_install(IRQ8_DF, (irq_handler_t)exception_handler_double_fault);
    irq_install(IRQ10_TS, (irq_handler_t)exception_handler_invalid_tss);
    irq_install(IRQ11_NP, (irq_handler_t)exception_handler_segment_not_present);
    irq_install(IRQ12_SS, (irq_handler_t)exception_handler_stack_segment_fault);
    irq_install(IRQ13_GP, (irq_handler_t)exception_handler_general_protection);
    irq_install(IRQ14_PF, (irq_handler_t)exception_handler_page_fault);
    irq_install(IRQ16_MF, (irq_handler_t)exception_handler_fpu_error);
    irq_install(IRQ17_AC, (irq_handler_t)exception_handler_alignment_check);
    irq_install(IRQ18_MC, (irq_handler_t)exception_handler_machine_check);
    irq_install(IRQ19_XM, (irq_handler_t)exception_handler_smd_exception);
    irq_install(IRQ20_VE, (irq_handler_t)exception_handler_virtual_exception);

    // init interrupt
    // init 8259A
    init_pic();

    lidt((uint32_t)idt_table, sizeof(idt_table));
}
/**
 * @brief 设置中断门
 */
void gate_desc_set(gate_desc_t *desc, uint16_t selector, uint32_t offset, uint16_t attr)
{
    desc->selector = selector;
    desc->attr = attr;
    desc->offset15_0 = offset & 0xffff;
    desc->offset31_16 = (offset >> 16) & 0xffff;
}
/**
 * @brief 注册中断处理程序
 */
int irq_install(int irq_num, irq_handler_t handler)
{
    if (irq_num >= IDE_TABLE_NR)
    {
        return -1;
    }

    gate_desc_set(idt_table + irq_num, KERNEL_SELECTOR_CS, (uint32_t)handler,
                  GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_INT);
}

// define pic_send_eoi function
/**
 * @brief 发送中断处理完成指令给中断处理芯片
 * @param irq_num 
 * @return * void 
 */
void pic_send_eoi(int irq_num)
{
    irq_num -= IRQ_PIC_START;
    if (irq_num >= 8)
    {
        outb(PIC1_OCW2, PIC_OCW2_EOI);
    }
    outb(PIC0_OCW2, PIC_OCW2_EOI);
}

// 定义开关中断，临界资源保护部分的内容
/**
 * @brief 进入中断保护
 */
irq_state_t irq_enter_protection(void){
    irq_state_t eflags = read_eflags();
    irq_disable_global();
    return eflags;
} /**
   * @brief 退出中断保护
   */
void irq_leave_protection(irq_state_t state)
{
    write_eflags(state);
}
